
package com.syu.loopcure.compensator.task;

import com.syu.loopcure.common.constants.LoopCoreGlobalConstant;
import com.syu.loopcure.common.utils.SpringBeanUtils;
import com.syu.loopcure.compensator.LoopCureCompensator;
import com.syu.loopcure.compensator.entity.ToolCureCompensator;
import com.syu.loopcure.compensator.enums.StatusEnum;
import com.syu.loopcure.compensator.interfaces.ICompensator;
import com.syu.loopcure.compensator.model.CompensatorInfoModel;
import com.syu.loopcure.compensator.service.ToolCureCompensatorService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;

/**  
 * ClassName:CompensatorTask <br/>  
 * Function: 补偿机制任务. <br/> 
 * Date:     2020年8月12日 下午4:44:03 <br/>  
 * @author   zhucun  
 * @since    JDK 1.8  
 */
@Component
public class CompensatorTask {
    
	/**
	 * 补偿机制执行超时时间限制（秒，默认1800）
	 */
    @Value("${loop-cure.timeout-max:1800}")
    private int timeoutMax;

    private final ToolCureCompensatorService toolCureCompensatorService;

    @Autowired
    public CompensatorTask(ToolCureCompensatorService toolCureCompensatorService){
        this.toolCureCompensatorService = toolCureCompensatorService;
    }
    
    /**
     * 
     * afterWaitSeconds:计算重试间隔，满足间隔本次方才拉起. <br/>  
     *  
     * @param waitSeconds 等待时间
     * @param retryTimes 尝试次数
     * @param nowTime 当前时间
     * @param retryEndTime 重试结束时间
     * @return  true：满足 false：不满足
     * @since JDK 1.8  
     * @author zhucun  
     * @date 2020年8月13日 下午2:46:40
     * @modifier zhucun
     * @modifyDate 2020年8月13日 下午2:46:40
     */
    private boolean afterWaitSeconds(String waitSeconds,int retryTimes,LocalDateTime nowTime,Date retryEndTime) {

        // 计算重试间隔，满足间隔本次方才拉起
        if(!StringUtils.isEmpty(waitSeconds)) {
        	String[] waitSecondsArray = waitSeconds.split(LoopCoreGlobalConstant.COMMA);
        	int waitSecondsArrayLength = waitSecondsArray.length;
        	if(0 != waitSecondsArrayLength) {
        		int waitSecondIndex = waitSecondsArrayLength >= retryTimes ? (retryTimes - 1) : (waitSecondsArrayLength -1);
        		// 本次重试间隔
        		String thisWaitSecond = waitSecondsArray[waitSecondIndex];
        		LocalDateTime retryEndTimeLocal = retryEndTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                return Duration.between(nowTime,
                        retryEndTimeLocal.plusSeconds(Integer.parseInt(thisWaitSecond))).isNegative();
        	}
        }
        return Boolean.FALSE;
    }
    
    /**
     * 
     * doCompensatorTask:一定时间间隔拉取一遍待补偿的业务（默认20秒）. <br/>  
     *    
     * @since JDK 1.8  
     * @author zhucun  
     * @date 2020年8月12日 下午5:31:06
     * @modifier zhucun
     * @modifyDate 2020年8月12日 下午5:31:06
     */
    @Scheduled(fixedRateString = "${loop-cure.task-fixedRate:20000}")
    public void doCompensatorTask() {
        // 拉取失败待补偿的数据
        List<ToolCureCompensator> toolCureCompensatorList = toolCureCompensatorService.getCompensatorRecords();
        if(CollectionUtils.isEmpty(toolCureCompensatorList)) {
            return;
        }
        Date retryStartTime;
        Date retryEndTime;
        LocalDateTime nowTime;
        LocalDateTime retryStartTimeLocal;
        // 执行补偿
        for (ToolCureCompensator toolCureCompensator : toolCureCompensatorList) {
            retryStartTime = toolCureCompensator.getRetryStartTime();
            retryEndTime = toolCureCompensator.getRetryEndTime();
            nowTime = LocalDateTime.now();
            // 已开始补偿并且尚未执行完成的数据，不参与本次拉起（已超时的未执行完成任务除外）
            if(StatusEnum.DOING_COMPENSATOR.getCode().equals(toolCureCompensator.getStatus())) {
                retryStartTimeLocal = retryStartTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                // 超时标志
                boolean timeOutFlag = Duration.between(nowTime, retryStartTimeLocal.plusSeconds(timeoutMax)).isNegative();
                if(!timeOutFlag) {
                    continue;
                }
            }
            // 已重试n次
            int retryTimes = toolCureCompensator.getRetryTimes();
            if(toolCureCompensator.getMaxRetryTimes() < retryTimes) {
            	continue;
            }
            // 计算重试间隔，满足间隔本次方才拉起
            if(null != retryStartTime && null != retryEndTime ) {
            	if(!afterWaitSeconds(toolCureCompensator.getWaitSeconds(),retryTimes,nowTime,retryEndTime)) {
            		continue;
            	}
            }
            // 组装待补偿数据实体
            CompensatorInfoModel compensatorInfoModel = new CompensatorInfoModel();
            BeanUtils.copyProperties(toolCureCompensator, compensatorInfoModel);
            // 执行方法
            LoopCureCompensator.newBuilder()
                .setCompensatorInterface(new ICompensator() {
                
                @Override
                public CompensatorInfoModel obtainCompensatorInfo() {
                    return compensatorInfoModel;
                }
                
                @Override
                public <T> T getBean(Class<T> clazz) {
                    
                    return SpringBeanUtils.getBean(clazz);
                }

				@Override
				public void updateInfoBeforeExecuteMethodByMd5Code(CompensatorInfoModel compensatorInfoModel) {
					// 更新数据
		            ToolCureCompensator upToolCureCompensator = new ToolCureCompensator();
		            BeanUtils.copyProperties(compensatorInfoModel, upToolCureCompensator);
		            toolCureCompensatorService.lambdaUpdate()
		            	.eq(ToolCureCompensator::getMd5Code, upToolCureCompensator.getMd5Code())
		            	.update(upToolCureCompensator);
				}

				@Override
				public void successUpdateInfoByMd5Code(CompensatorInfoModel compensatorInfoModel) {
					// 更新数据
		            ToolCureCompensator upToolCureCompensator = new ToolCureCompensator();
		            BeanUtils.copyProperties(compensatorInfoModel, upToolCureCompensator);
		            toolCureCompensatorService.lambdaUpdate()
		            	.eq(ToolCureCompensator::getMd5Code, upToolCureCompensator.getMd5Code())
		            	.update(upToolCureCompensator);
				}
            }).doCompensator();
        }
    }
}
  
